﻿using System;
using System.Collections.Generic;
using EmbeddedSharp.Core.Function.Arithmetics;
using EmbeddedSharp.Core.Function.Logic;
using EmbeddedSharp.Core.Function.Maths;

namespace EmbeddedSharp.Core {
    /// <summary>
    /// Expression parser
    /// </summary>
    public class ExpressionParser : IExpressionParser {
        /// <summary>
        /// The number of left parethesis went through
        /// </summary>
        protected int currentParethesisDepth;

        /// <summary>
        /// Lexer
        /// </summary>
        protected Lexer lexer;

        /// <summary>
        /// Operand stack
        /// </summary>
        protected Stack<Node> operandStack;

        /// <summary>
        /// Operator stack
        /// </summary>
        protected Stack<Node> operatorStack;

        public IFunctionManager FunctionManager { get; set; }
        /// <summary>
        /// Constructor for internal use only
        /// </summary>
        private ExpressionParser() {
            operandStack = new Stack<Node>();
            operatorStack = new Stack<Node>();
            ParameterCountStack
                = new Stack<ParameterCountStruct>();
            Variables = new List<Node>();
        }

        /// <summary>
        /// Default constructor
        /// </summary>
        /// <param name="buff"></param>
        public ExpressionParser(string buff)
            : this() {
            lexer = new Lexer(buff);
        }

        /// <summary>
        /// Default constructor
        /// </summary>
        /// <param name="lexer"></param>
        public ExpressionParser(Lexer lexer)
            : this() {
            this.lexer = lexer;
        }

        /// <summary>
        /// Number of parameters for the current stack entry
        /// </summary>
        protected Stack<ParameterCountStruct> ParameterCountStack { get; set; }

        /// <summary>
        /// Expression parser factory
        /// </summary>
        public IExpressionParserFactory ExpressionParserFactory { get; set; }

        /// <summary>
        /// Function manager
        /// </summary>
        /// <summary>
        /// Constant manager
        /// </summary>
        public IConstantManager ConstantManager { get; set; }

        /// <summary>
        /// Variables
        /// </summary>
        public List<Node> Variables { get; set; }

        /// <summary>
        /// Do the parse
        /// </summary>
        /// <returns></returns>
        public Node Parse() {
            Token token;
            Node root;

            while (lexer.HasMoreTokens()) {
                token = lexer.NextToken();
                switch (token.TokenType) {
                    case TokenType.IDENTIFIER:
                        char nc = lexer.PeekChar();
                        if (nc == '(') {
                            FuncNode fn = FunctionManager.GetFunction(token.Value) ??
                                          CannotFindSuitableFunction(token.Value);

                            operatorStack.Push(fn);
                            var pcs
                                = new ParameterCountStruct
                                      {
                                          numberOfParameters = 0,
                                          parethesisDepth = currentParethesisDepth + 1
                                      };

                            ParameterCountStack.Push(pcs);
                        } else if (nc == '[') {
                        } else if (token.Value == "nil") {
                            var nn = new NilNode();
                            operandStack.Push(nn);
                        } else {
                            //if (ConstantManager.IsConstant(token.Value))
                            //{
                            //    root = new NumberNode {Value = ConstantManager.GetConstant(token.Value)};
                            //}
                            //else
                            //{
                            root = GetVariableNode(token.Value);
                            root.Value = token.Value;

                            //if (!this.Identifiers.Contains(token.Value))
                            //{
                            //    this.Identifiers.Add(token.Value);
                            //}
                            if (!Variables.Contains(root)) {
                                Variables.Add(root);
                            }
                            //}

                            operandStack.Push(root);
                        }
                        break;
                    case TokenType.Number:
                        root = new NumberNode { Value = token.Value };
                        operandStack.Push(root);
                        break;
                    case TokenType.Assign:
                        root = CreateAssignNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.Declare:
                        root = CreateDeclareNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.Add:
                        root = new AddNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.Sub:
                        if (lexer.LastToken == null
                            || lexer.LastToken.TokenType == TokenType.LeftBrace
                            || lexer.LastToken.TokenType == TokenType.Comma
                            ) {
                            root = new NumberNode { Value = "0" };
                            operandStack.Push(root);
                            // there is nothing ahead
                            // it is a negative number
                        }
                        root = new SubNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.Mul:
                        root = new MulNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.Div:
                        root = new DivNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.Percent:
                        root = new ModNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.Square:
                        root = new PowerNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.Tilde:
                        root = new LogNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.And:
                        root = new AndNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.Or:
                        root = new OrNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.Not:
                        root = new NotNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.Nand:
                        root = new NandNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.Nor:
                        root = new NorNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.Equal:
                        root = new EqualNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.NotEqual:
                        root = new NotEqualNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.LargerEqualTo:
                        root = new LargerEqualToNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.LargerThan:
                        root = new LargerThanNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.LessEqualTo:
                        root = new LessEqualToNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.LessThan:
                        root = new LessThanNode();
                        ProcessAllOperatorsWhosePrecedenceIsHigerThan(
                            ((FuncNode)root).Precedence);
                        operatorStack.Push(root);
                        break;
                    case TokenType.LeftSquareBrace:
                        var pcc = new ParameterCountStruct
                                  {
                                      numberOfParameters = 0,
                                      parethesisDepth = currentParethesisDepth + 1
                                  };

                        operatorStack.Push(new ListNode());
                        currentParethesisDepth++;

                        ParameterCountStack.Push(pcc);
                        operatorStack.Push(new EmbraceNode());
                        Parse();
                        //stack.Push(root);
                        break;
                    case TokenType.LeftBrace:
                        currentParethesisDepth++;
                        //this.operatorNodeStack.Push(new GroupNode());
                        operatorStack.Push(new EmbraceNode());
                        Parse();
                        //stack.Push(root);
                        break;
                    case TokenType.RightBracket:
                    case TokenType.RightSquareBrace:
                    case TokenType.RightBrace:
                        if (operatorStack.Peek() != null) {
                            // okay, let's finish the last expression
                            // until EmbraceNode

                            bool done;
                            do {
                                done = ProcessAllOperatorsWhosePrecedenceIsHigerThan(-1);
                            } while (!done);

                            if (ParameterCountStack.Count > 0) {
                                ParameterCountStruct p
                                    = ParameterCountStack.Pop();
                                if (p.parethesisDepth == currentParethesisDepth) {
                                    // let's check out whether the number of 
                                    // parameters is zero or one
                                    //char lastChar = lexer.PreviousChar(2);
                                    if (lexer.LastToken.TokenType == TokenType.LeftBrace
                                        || lexer.LastToken.TokenType == TokenType.LeftSquareBrace) {
                                        p.numberOfParameters = 0;
                                    } else {
                                        p.numberOfParameters++;
                                    }
                                }
                                ParameterCountStack.Push(p);
                            }
                            currentParethesisDepth--;
                        }
                        return null;
                    case TokenType.Comma:
                        if (operatorStack.Peek() != null) {
                            bool done;
                            do {
                                done = ProcessAllOperatorsWhosePrecedenceIsHigerThan(-1);
                            } while (!done);
                        }
                        if (ParameterCountStack.Count > 0) {
                            ParameterCountStruct pcx = ParameterCountStack.Pop();
                            if (pcx.parethesisDepth == currentParethesisDepth) {
                                pcx.numberOfParameters++;
                            }
                            ParameterCountStack.Push(pcx);
                        }

                        operatorStack.Push(new EmbraceNode());
                        break;
                    case TokenType.Quote:
                        var node = new StringNode { Value = lexer.ReadUntil('\"') };
                        operandStack.Push(node);
                        break;
                    case TokenType.Comment:
                        break;
                    case TokenType.Semicolon:
                        break;
                    case TokenType.End:
                        break;
                    default:
                        Parse(token);
                        break;
                }
            }

            if (operatorStack.Count > 0) {
                do {
                    ProcessAllOperatorsWhosePrecedenceIsHigerThan(-1);
                } while (operandStack.Count > 1);
            }
            return operandStack.Pop();
        }

        /// <summary>
        /// Reimplement this function in case the given name cannot be recoginzied as a function
        /// </summary>
        /// <param name="p"></param>
        protected virtual FuncNode CannotFindSuitableFunction(string p) {
            throw new FunctionNotDefinedException(p);
        }

        /// <summary>
        /// Parse the exceptional token
        /// </summary>
        /// <param name="token">the exceptional token</param>
        protected virtual void Parse(Token token) {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected virtual Node CreateDeclareNode() {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Create an assigned node
        /// </summary>
        /// <returns></returns>
        protected virtual Node CreateAssignNode() {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Attach child nodes to the parent node
        /// </summary>
        protected void AttachChildNodeToParentOperatorNode() {
            Node current = operandStack.Pop();
            Node on = operatorStack.Pop();
            if (on.GetType().IsSubclassOf(typeof(FuncNode))) {
                var funcNode
                    = (FuncNode)on;
                funcNode.Parameters.Add(current);
            }

            operatorStack.Push(on);
        }

        /// <summary>
        /// Post process the identifier node
        /// </summary>
        /// <param name="name">the name of the variable</param>
        public virtual Node GetVariableNode(string name) {
            var node = new IdentifierNode { Value = name };

            return node;
        }

        /// <summary>
        /// Consume all the operators to form an expression until reach the lower precedence operator
        /// </summary>
        /// <param name="precedenceOfTheUpcomingOperator">the terminal precedence</param>
        private bool ProcessAllOperatorsWhosePrecedenceIsHigerThan(
            int precedenceOfTheUpcomingOperator) {
            if (operatorStack.Count == 0) {
                return false;
            }

            // get the current operator
            var currentOperator
                = (FuncNode)operatorStack.Peek();
            if (currentOperator.GetType().Equals(
                typeof(EmbraceNode))) {
                if (precedenceOfTheUpcomingOperator == -1) {
                    // if the most recent expression is finished
                    operatorStack.Pop();
                    return true;
                }

                return false;
            }

            // test if we need to process the current operator
            while (currentOperator.Precedence
                   >= precedenceOfTheUpcomingOperator) {
                // if the current operator has higher precedence
                // of the upcoming one
                operatorStack.Pop();
                if (currentOperator is UnaryOperatorNode) {
                    var son
                        = (UnaryOperatorNode)currentOperator;
                    son.Operand = operandStack.Pop();
                } else if (currentOperator is OperatorNode) {
                    var on
                        = (OperatorNode)currentOperator;
                    on.Right = operandStack.Pop();
                    on.Left = operandStack.Pop();
                } else if (currentOperator is FuncNode) {
                    var fn = currentOperator;
                    //int order = fn.ParameterCount;

                    ParameterCountStruct pcs = ParameterCountStack.Pop();
                    for (int index = 0; index < pcs.numberOfParameters; index++) {
                        Node child = operandStack.Pop();
                        fn.Parameters.Add(child);
                    }
                }
                operandStack.Push(currentOperator);
                if (operatorStack.Count > 0) {
                    currentOperator = (FuncNode)operatorStack.Peek();
                    if (currentOperator is EmbraceNode) {
                        break;
                    }
                } else {
                    break;
                }
            }

            return false;
        }
    }
}